﻿package com.cw.utils.dll
{
	import flash.display.DisplayObjectContainer;
	import flash.display.Loader;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLRequest;
	import flash.net.URLStream;
	import flash.system.ApplicationDomain;
	import flash.system.LoaderContext;
	import flash.utils.ByteArray;
	import flash.utils.getTimer;
	import flash.utils.setTimeout;

	/**
	 * 当dll文件路径出错时发送此事件。
	 * @eventType com.gjy.dll.DLLLoader.IO_ERROR
	 */
	[Event(name="IO_ERROR", type="com.cw.utils.dll.DLLLoader")]

	/**
	 * 当服务器返回HTTP Code时发送此事件。
	 * @eventType com.gjy.dll.DLLLoader.HTTP_STATUS
	 */
	[Event(name="HTTP_STATUS", type="com.cw.utils.dll.DLLLoader")]

	/**
	 * 当出现安全沙箱错误时发送此事件。
	 * @eventType com.gjy.dll.DLLLoader.SECURITY_ERROR
	 */
	[Event(name="SECURITY_ERROR", type="com.cw.utils.dll.DLLLoader")]

	/**
	 * 当开始加载dll文件时发送此事件。
	 * @eventType com.gjy.dll.DLLLoader.OPEN
	 */
	[Event(name="OPEN", type="com.cw.utils.dll.DLLLoader")]

	/**
	 * 当正在加载dll文件时发送此事件。
	 * @eventType com.gjy.dll.DLLLoader.PROGRESS
	 */
	[Event(name="PROGRESS", type="com.cw.utils.dll.DLLLoader")]

	/**
	 * 当加载完一个dll文件时发送此事件。
	 * @eventType com.gjy.dll.DLLLoader.COMPLETE
	 */
	[Event(name="COMPLETE", type="com.cw.utils.dll.DLLLoader")]

	/**
	 * 在把一个dll文件加载到当前应用程序域前发送此事件。
	 * @eventType com.gjy.dll.DLLLoader.DLL_INSTALL
	 */
	[Event(name="DLL_INSTALL", type="com.cw.utils.dll.DLLLoader")]

	/**
	 * 当下载完所有dll文件时发送后发送此事件。
	 * @eventType com.gjy.dll.DLLLoader.ALL_COMPLETED
	 */
	[Event(name="ALL_COMPLETED", type="com.cw.utils.dll.DLLLoader")]

	/**
	 * DLL加载器，用于加载作为DLL的swf文件。对象会将DLL文件加载到同域，即(ApplicationDomain.currentDomain)，使得主程序与子程序可以共享各自的对象。
	 * @author DanielLu
	 */
	public class DLLLoader extends EventDispatcher
	{
		// -------------------------------------------------------------------------------- events
		/**
		 *  <code>DLLLoader.IO_ERROR</code> 常量定义 <code>ioError</code> 事件对象的<code>type</code>属性值.
		 *
		 *  <p>此事件具有以下属性：</p>
		 *  <table class="innertable">
		 *     <tr><th>属性</th><th>值</th></tr>
		 *     <tr><td><code>bubbles</code></td><td>false</td></tr>
		 *     <tr><td><code>cancelable</code></td><td>false；没有要取消的默认行为。</td></tr>
		 *     <tr><td><code>currentTarget</code></td><td>当前正在使用某个事件侦听器处理 Event 对象的对象。 </td></tr>
		 *     <tr><td><code>target</code></td><td>发生输入/输出错误的网络对象。</td></tr>
		 *     <tr><td><code>text</code></td><td>要显示为错误消息的文本。</td></tr>
		 *  </table>
		 *  @eventType IO_ERROR
		 */
		public static const IO_ERROR : String = IOErrorEvent.IO_ERROR;

		/**
		 * <code>DLLLoader.HTTP_STATUS</code> 常量定义 <code>httpStatus</code> 事件对象的<code>type</code>属性值.
		 *
		 *  <p>此事件具有以下属性：</p>
		 *  <table class="innertable">
		 *     <tr><th>属性</th><th>值</th></tr>
		 *     <tr><td><code>bubbles</code></td><td>false</td></tr>
		 *     <tr><td><code>cancelable</code></td><td>false；没有要取消的默认行为。</td></tr>
		 *     <tr><td><code>currentTarget</code></td><td>当前正在使用某个事件侦听器处理 Event 对象的对象。 </td></tr>
		 *     <tr><td><code>status</code></td><td>由服务器返回的 HTTP 状态代码。</td></tr>
		 *     <tr><td><code>target</code></td><td>接收 HTTP 状态代码的网络对象。 </td></tr>
		 *  </table>
		 *
		 *  @eventType HTTP_STATUS
		 */
		public static const HTTP_STATUS : String = HTTPStatusEvent.HTTP_STATUS;
		/**
		 *  <code>DLLLoader.SECURITY_ERROR</code> 常量定义 <code>securityError</code> 事件对象的<code>type</code>属性值.
		 *
		 *  <p>此事件具有以下属性：</p>
		 *  <table class="innertable">
		 *     <tr><th>属性</th><th>值</th></tr>
		 *     <tr><td><code>bubbles</code></td><td>false</td></tr>
		 *     <tr><td><code>cancelable</code></td><td>false；没有要取消的默认行为。</td></tr>
		 *     <tr><td><code>currentTarget</code></td><td>当前正在使用某个事件侦听器处理 Event 对象的对象。 </td></tr>
		 *     <tr><td><code>target</code></td><td>报告安全错误的网络对象。</td></tr>
		 *     <tr><td><code>text</code></td><td>要显示为错误消息的文本。 </td></tr>
		 *  </table>
		 *
		 *  @eventType SECURITY_ERROR
		 */
		public static const SECURITY_ERROR : String = SecurityErrorEvent.SECURITY_ERROR;

		/**
		 *  <code>DLLLoader.OPEN</code> 常量定义 <code>open</code> 事件对象的<code>type</code>属性值.
		 *
		 *  <p>此事件具有以下属性：</p>
		 *  <table class="innertable">
		 *     <tr><th>属性</th><th>值</th></tr>
		 *     <tr><td><code>bubbles</code></td><td>false</td></tr>
		 *     <tr><td><code>cancelable</code></td><td>false；没有要取消的默认行为。</td></tr>
		 *     <tr><td><code>currentTarget</code></td><td>当前正在使用某个事件侦听器处理 Event 对象的对象。 </td></tr>
		 *     <tr><td><code>target</code></td><td>已打开连接的网络对象。 </td></tr>
		 *  </table>
		 *
		 *  @eventType OPEN
		 */
		public static const OPEN : String = Event.OPEN;

		/**
		 *  <code>DLLLoader.PROGRESS</code> 常量定义 <code>progress</code> 事件对象的<code>type</code>属性值.
		 *
		 *  <p>此事件具有以下属性：</p>
		 *  <table class="innertable">
		 *     <tr><th>属性</th><th>值</th></tr>
		 *     <tr><td><code>bubbles</code></td><td>false</td></tr>
		 *     <tr><td><code>bytesLoaded</code></td><td>在侦听器处理事件时加载的项数或字节数。</td></tr>
		 *     <tr><td><code>bytesTotal</code></td><td>如果加载进程成功，最终将加载的项目或字节总数。</td></tr>
		 *     <tr><td><code>cancelable</code></td><td>false；没有要取消的默认行为。</td></tr>
		 *     <tr><td><code>currentTarget</code></td><td>当前正在使用某个事件侦听器处理 Event 对象的对象。</td></tr>
		 *     <tr><td><code>target</code></td><td>报告进度的网络对象。 </td></tr>
		 *  </table>
		 *
		 *  @eventType PROGRESS
		 */
		public static const PROGRESS : String = ProgressEvent.PROGRESS;

		/**
		 *  <code>DLLLoader.COMPLETE</code> 常量定义 <code>complete</code> 事件对象的<code>type</code>属性值.
		 *
		 *  <p>此事件具有以下属性：</p>
		 *  <table class="innertable">
		 *     <tr><th>属性</th><th>值</th></tr>
		 *     <tr><td><code>bubbles</code></td><td>false</td></tr>
		 *     <tr><td><code>cancelable</code></td><td>false；没有要取消的默认行为。</td></tr>
		 *     <tr><td><code>currentTarget</code></td><td>当前正在使用某个事件侦听器处理 Event 对象的对象。 </td></tr>
		 *     <tr><td><code>target</code></td><td>已完成加载的网络对象。 </td></tr>
		 *  </table>
		 *
		 *  @eventType COMPLETE
		 */
		public static const COMPLETE : String = Event.COMPLETE;

		/**
		 *  <code>DLLLoader.DLL_INSTALL</code> 常量定义 <code>dllInstall</code> 事件对象的<code>type</code>属性值.
		 *
		 *  <p>此事件具有以下属性：</p>
		 *  <table class="innertable">
		 *     <tr><th>属性</th><th>值</th></tr>
		 *     <tr><td><code>bubbles</code></td><td>false</td></tr>
		 *     <tr><td><code>cancelable</code></td><td>false；没有要取消的默认行为。</td></tr>
		 *     <tr><td><code>currentTarget</code></td><td>当前正在使用某个事件侦听器处理 Event 对象的对象。 </td></tr>
		 *     <tr><td><code>target</code></td><td><code>DLLLoader</code>对象。 </td></tr>
		 *  </table>
		 *
		 *  @eventType DLL_INSTALL
		 */
		public static const DLL_INSTALL : String = "dllInstall";

		/**
		 *  <code>DLLLoader.ALL_COMPLETED</code> 常量定义 <code>allCompleted</code> 事件对象的<code>type</code>属性值.
		 *
		 *  <p>此事件具有以下属性：</p>
		 *  <table class="innertable">
		 *     <tr><th>属性</th><th>值</th></tr>
		 *     <tr><td><code>bubbles</code></td><td>false</td></tr>
		 *     <tr><td><code>cancelable</code></td><td>false；没有要取消的默认行为。</td></tr>
		 *     <tr><td><code>currentTarget</code></td><td>当前正在使用某个事件侦听器处理 Event 对象的对象。 </td></tr>
		 *     <tr><td><code>target</code></td><td>完成DLL加载的<code>DLLLoader</code>对象。 </td></tr>
		 *  </table>
		 *
		 *  @eventType ALL_COMPLETED
		 */
		public static const ALL_COMPLETED : String = "allCompleted";

		// -------------------------------------------------------------------------------- private
		private var dllList : Array;
		private var currentDLL : DLLVo;

		private var dllCount : int;
		private var dllLoadedCount : int;
		private var faultCount : int;
		private var startTime : Number;
		private var currentSpeed : Number;

		private var root : DisplayObjectContainer;

		// 加载对象
		private var stream : URLStream;
		private var loader : Loader;

		/**
		 * 创建一个<code>DLLLoader</code>对象。
		 * @param root		主显示对象。
		 */
		public function DLLLoader ( root : DisplayObjectContainer = null )
		{
			this.dllList = new Array ();

			this.dllCount = 0;
			this.dllLoadedCount = 0;
			this.faultCount = 0;
			this.startTime = 0;
			this.currentSpeed = 0;

			this.root = root;

			this.stream = new URLStream ();
			this.stream.addEventListener ( IOErrorEvent.IO_ERROR , this.__onIOError );
			this.stream.addEventListener ( SecurityErrorEvent.SECURITY_ERROR , this.__onSecurityError );
			this.stream.addEventListener ( HTTPStatusEvent.HTTP_STATUS , this.__onHttpStatus );
			this.stream.addEventListener ( Event.OPEN , this.__onOpen );
			this.stream.addEventListener ( ProgressEvent.PROGRESS , this.__onProgress );
			this.stream.addEventListener ( Event.COMPLETE , this.__onStreamCompleted );


			this.loader = new Loader ();
			this.loader.contentLoaderInfo.addEventListener ( Event.COMPLETE , this.__onLoaderCompleted );
		}

		/**
		 * 添加一个dll。
		 * @param path					dll资源文件路径
		 * @param displayName			dll资源名称
		 * @param executeMethodName		加载dll之后需要运行的方法
		 * @param params				加载dll之后需要运行的方法参数，使用逗号分隔多个参数
		 */
		public function addDLL ( path : String , displayName : String = null , executeMethodName : String = null , ...params ) : void
		{
			var dll : DLLVo = new DLLVo ();
			dll.path = path;
			dll.displayName = displayName;
			dll.executeMethodName = executeMethodName;
			dll.params = params;
			this.dllList.push ( dll );
			this.dllCount ++;
		}

		/**
		 * 返回当前dll的路径。如果当前没有进行加载则返回<code>null</code>。
		 * @return		当前DLL的路径。
		 */
		public function getCurrentPath () : String
		{
			return this.currentDLL ? this.currentDLL.path : null;
		}

		/**
		 * 返回当前dll的名称。如果当前没有进行加载则返回<code>null</code>。
		 * @return		当前DLL的名称。
		 */
		public function getCurrentDisplayName () : String
		{
			return this.currentDLL ? this.currentDLL.displayName : null;
		}

		/**
		 * 返回当前dll的执行方法名称。如果当前没有进行加载则返回<code>null</code>。
		 * @return		当前DLL的执行方法名称。
		 */
		public function getCurrentExecuteMethodName () : String
		{
			return this.currentDLL ? this.currentDLL.executeMethodName : null;
		}

		/**
		 * 返回要加载的dll的个数。
		 * @return		需要加载的dll的个数。
		 */
		public function getDLLCount () : int
		{
			return this.dllCount;
		}

		/**
		 * 返回已加载的dll的个数。
		 * @return		已加载的dll的个数。
		 */
		public function getDLLLoadedCount () : int
		{
			return this.dllLoadedCount;
		}

		/**
		 * 返回当前的加载速度(字节/秒)。
		 * @return		当前的加载速度(字节/秒)。
		 */
		public function getCurrentSpeed () : Number
		{
			return this.currentSpeed;
		}

		/**
		 * 开始加载dll文件。
		 */
		public function doLoad () : void
		{
			var dll : DLLVo = this.dllList.shift () as DLLVo ;
			//
			if (dll == null)
			{
				this.dispatchEvent ( new Event ( ALL_COMPLETED ) );
			}
			else
			{
				this.faultCount = 0;
				this.currentDLL = dll;
				this.loadDLL ();
			}
		}

		/**
		 * 加载dll
		 */
		private function loadDLL () : void
		{
			var req : URLRequest = new URLRequest ( this.currentDLL.path );
			this.stream.load ( req );
		}

		/**
		 * 通过字节加载dll
		 */
		private function loadDLLBytes ( bytes : ByteArray ) : void
		{
			var context : LoaderContext = new LoaderContext ( false , ApplicationDomain.currentDomain );
			this.loader.loadBytes ( bytes , context );
		}

		// -------------------------------------------------------------------------------- listener
		private function __onIOError ( evt : IOErrorEvent ) : void
		{
			this.__onError ( evt );
		}

		private function __onSecurityError ( evt : SecurityErrorEvent ) : void
		{
			this.__onError ( evt );
		}

		private function __onHttpStatus ( evt : HTTPStatusEvent ) : void
		{
			this.dispatchEvent ( evt );
		}

		private function __onError ( evt : Event ) : void
		{
			this.faultCount ++;
			if (this.faultCount >= 3)
			{
				this.dispatchEvent ( evt );
			}
			else
			{
				setTimeout ( function () : void
				{
					loadDLL ();
				} , 500 );
			}
		}

		private function __onOpen ( evt : Event ) : void
		{
			this.startTime = getTimer ();
			this.dispatchEvent ( evt );
		}

		private function __onProgress ( evt : ProgressEvent ) : void
		{
			this.currentSpeed = evt.bytesLoaded / Math.round ( (getTimer () - this.startTime) / 1000 );
			this.dispatchEvent ( evt );
		}

		private function __onStreamCompleted ( evt : Event ) : void
		{
			var bytes : ByteArray = new ByteArray ();
			this.stream.readBytes ( bytes , 0 , bytes.bytesAvailable );
			this.stream.close ();
			this.dispatchEvent ( new Event ( DLL_INSTALL ) );
			this.loadDLLBytes ( bytes );
		}

		private function __onLoaderCompleted ( evt : Event ) : void
		{
			this.dllLoadedCount ++;
			//
			this.dispatchEvent ( evt );
			if (this.currentDLL.executeMethodName != null)
			{
				var args : Array = [ this.root ].concat ( this.currentDLL.params );
				this.loader.content[this.currentDLL.executeMethodName].apply ( null , args );
			}
			// 继续加载
			this.doLoad ();
		}
	}
}

class DLLVo
{
	public var path : String;
	public var displayName : String;
	public var executeMethodName : String;
	public var params : Array;
}
